package gek.uni.diplom.ocl.example.generators.visitors.hierarhical;

import gek.uni.diplom.ocl.example.exceptions.ErrorException;
import gek.uni.diplom.ocl.example.exceptions.NotImplementedMethodException;
import gek.uni.diplom.ocl.example.managers.ErrorManager;
import gek.uni.diplom.ocl.example.patterns.hierarhical.AChangeableCodeNode;
import gek.uni.diplom.ocl.example.patterns.hierarhical.BooleanExpCodeNode;
import gek.uni.diplom.ocl.example.patterns.hierarhical.BooleanLiteralCodeNode;
import gek.uni.diplom.ocl.example.patterns.hierarhical.IPattern;
import gek.uni.diplom.ocl.example.patterns.hierarhical.IntegerExpCodeNode;
import gek.uni.diplom.ocl.example.patterns.hierarhical.VariableExpCodeNode;
import gek.uni.diplom.ocl.example.utils.GeneratorUtils;
import gek.uni.diplom.ocl.example.utils.LogManager;
import gek.uni.diplom.ocl.example.utils.OclEcoreUtils;

import java.util.List;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ocl.OCL;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.ecore.internal.OCLStandardLibraryImpl;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.types.PrimitiveType;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.PredefinedType;

/**
 * Все же нужно генерировать и для истинного утверждения и для ложного. <br/>
 * ((a+b) = 3 = false)
 * 
 * 
 * @author Gek
 * 
 */
@SuppressWarnings("restriction")
public class PatternGeneratorVisitor
    extends
    AbstractVisitor<IPattern, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> {

  OCL<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> ocl;

  public PatternGeneratorVisitor() {
    ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);
  }

  @Override
  public AChangeableCodeNode visitVariableExp(
      VariableExp<EClassifier, EParameter> v) {
    EClassifier type = v.getType();
    String varName = v.getName();

    AChangeableCodeNode ptrn = new VariableExpCodeNode(v.toString(), null);

    String[] declarationStrings =
        GeneratorUtils.createVariableDeclarationString(type, varName);
    String declaration = declarationStrings[0];
    // String initialization = declarationStrings[1];
    ptrn.setDeclarationString(declaration);
    // ptrn.addInitializationSection(varName, initialization);
    for (int i = 2; i < declarationStrings.length; i++) {
      ptrn.addImportString(declarationStrings[i]);
    }
    // ptrn.getDeclaredVariableNames().add(varName);
    // ptrn.setReference(varName, varName);

    String[] variableGetterStrings =
        GeneratorUtils.createVariableGetterCode(v.getName());
    String[] variableSetterStrings =
        GeneratorUtils.createVariableSetterStrings(v.getName(), (EClass) v
            .getType());

    // ptrn.setSetterInitString(variableSetterStrings[0]);
    ptrn.setDeclarationString(variableSetterStrings[0]);
    ptrn.setSetterCallString(variableSetterStrings[1]);
    ptrn.setGetterInitString(variableGetterStrings[0]);
    ptrn.setGetterCallString(variableGetterStrings[1]);
    return ptrn;
  }

  @Override
  public IPattern visitBooleanLiteralExp(
      BooleanLiteralExp<EClassifier> literalExp) {
    return new BooleanLiteralCodeNode(literalExp.toString(), null, literalExp
        .getBooleanSymbol());
  }

  @Override
  protected IPattern handleOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceRes,
      List<IPattern> argumentResults) {
    IPattern sourceResult = sourceRes;

    // sourceResult.getGetterInitString(callExp.toString());
    // sourceResult.getSetterInitString(callExp.toString());
    // OclEcoreUtils.copyPatternInitSections(sourceResult, callExp.getSource()
    // .toString(), callExp.toString());
    // TODO добавить строки инициализации переменных из правой части

    EClassifier typeOfExp = callExp.getType();
    if (typeOfExp.equals(OCLStandardLibraryImpl.INSTANCE.getBoolean()))
      /* && !OclEcoreUtils.checkIsLeafExp(callExp) */
      try {
        return handleBooleanOperationCallExp(callExp, sourceResult,
            argumentResults);
      } catch (ErrorException e) {
        ErrorManager.addError(e.getMessage());// e.printStackTrace();
      } catch (NotImplementedMethodException e) {
        ErrorManager.addError(e.getMessage());// e.printStackTrace();
      }
    else if (typeOfExp.equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
      try {
        return handleIntegerOperationCallExp(callExp, sourceResult,
            argumentResults);
      } catch (ErrorException e) {
        e.printStackTrace();
      }
    }

    return sourceRes;
  }

  // ==========================================================================

  @Override
  protected IPattern handlePropertyCallExp(
      PropertyCallExp<EClassifier, EStructuralFeature> callExp,
      IPattern sourceResult, List<IPattern> qualifierResults) {

    AChangeableCodeNode result =
        new VariableExpCodeNode(callExp.toString(), null);
    sourceResult.setParent(result);

    OCLExpression<EClassifier> source = callExp.getSource();

    EStructuralFeature property = callExp.getReferredProperty();

    String[] propertyGetterStrings =
        GeneratorUtils.generateProperyGetterStrings(source, property,
            (AChangeableCodeNode) sourceResult);

    String exprStr = callExp.toString();

    result.setGetterInitString(propertyGetterStrings[1]);
    result.setGetterCallString(propertyGetterStrings[2]);

    String[] propertySetterStrings =
        GeneratorUtils.generatePropertySetterStrings(callExp, property,
            (AChangeableCodeNode) sourceResult, propertyGetterStrings[0]);

    result.setSetterInitString(propertySetterStrings[0]);
    result.setSetterCallString(propertySetterStrings[1]);

    return result;
  }

  // ==========================================================================

  private IPattern handleBooleanOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults) throws ErrorException,
      NotImplementedMethodException {

    // Результат обработки аргументов добавим с созданному
    BooleanExpCodeNode result = new BooleanExpCodeNode(callExp.toString());
    sourceResult.setParent(result);

    for (IPattern argResult : argumentResults) {
      argResult.setParent(result);
    }

    // // Добавляем строки кода, импортирующие необходимые классы для вычислеия
    // // правой части выражения
    int opCode = callExp.getOperationCode();

    // Определяем код операции и генерируем 2 варианта инициализации переменной:
    // 1 - Чтобы опрерация возвращала true
    // 2 - Чтобы опреация возвращала false
    switch (opCode) {
    case PredefinedType.OCL_IS_UNDEFINED:
      return handleOclIsUndefinedExpression(callExp, sourceResult,
          argumentResults, result);
    case PredefinedType.EQUAL:
      return handleEqualOperationCallExp(callExp, sourceResult,
          argumentResults, result);
    case PredefinedType.LESS_THAN:
      return handleOclLessCallExp(callExp, sourceResult, argumentResults,
          result);
    case PredefinedType.LESS_THAN_EQUAL:
      return handleOclLessThanEqualCallExp(callExp, sourceResult,
          argumentResults, result);
    default:
      break;
    }

    return result;
  }

  /**
   * Генерация шаблонов кода для операций, которые возвращают Integer:<br/>
   * size(), count(),....
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException 
   */
  private AChangeableCodeNode handleIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults) throws ErrorException {
    IntegerExpCodeNode result = new IntegerExpCodeNode(callExp.toString());

    int opCode = callExp.getOperationCode();
    OCLExpression<EClassifier> source = callExp.getSource();
    switch (opCode) {
    case PredefinedType.SIZE:
      return handleSizeOperationCallExp(callExp, sourceResult, argumentResults,
          result);
    default:
      break;
    }
    return null;
  }

  /**
   * Генерация шаблонов кода для операции size()
   * 
   * @param callExp
   *          - операция size()
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException 
   */
  private AChangeableCodeNode handleSizeOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, IntegerExpCodeNode result) throws ErrorException {
    OCLExpression<EClassifier> source = callExp.getSource();
    EClassifier sourceType = source.getType();
    if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getSequence())) {
      // TODO Генерация кода для списка
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getSet())) {
      // TODO Генерация кода для множества
    } else if (sourceType.getInstanceClassName().equals(
        OCLStandardLibraryImpl.INSTANCE.getOrderedSet().getInstanceClassName())) {
      // TODO Генерация кода для операции size для упорядоченного множества
      return handleSizeOperationForOrderedSer(callExp, sourceResult, argumentResults,
          result);
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE
        .getOrderedSet())) {
      // TODO Генерация кода для упорядоченного множества
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getBag())) {
      // TODO Генерация кода для упорядоченного упорядоченного списка
    } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
      // TODO Генерация кода для строки
    }

    return result;
  }

  private AChangeableCodeNode handleSizeOperationForOrderedSer(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, IntegerExpCodeNode result)
      throws ErrorException {

    // Создать временную переменную добавить в неё объектов и присвоить исходной
    // коллекции
    EClassifier collectionType = callExp.getSource().getType();

    String tempVarName = GeneratorUtils.getUniqueName("orderSet_");

    String[] tempCollectionDeclarationString =
        GeneratorUtils.createVariableDeclarationString(collectionType,
            tempVarName);
    String[] tempCollectionGetterCode =
        GeneratorUtils.createVariableGetterCode(tempVarName);
    String[] tempCollectionSetterStrings =
        GeneratorUtils.createVariableSetterStrings(tempVarName,
            collectionType);
    String collectionInitializationString =
        GeneratorUtils.createNonNullObjectInitializationString(collectionType);

    StringBuilder bldr = new StringBuilder();
    bldr.append(tempCollectionDeclarationString[0]).append("\n");
    bldr.append(tempCollectionSetterStrings[1].replace(
        GeneratorUtils.REPLACE_CODE_STRING, collectionInitializationString));

    // Присваиваем sourceCollection'у созданную переменную
    AChangeableCodeNode sourceNode = (AChangeableCodeNode) sourceResult;
    String sourceGetterCallString = sourceNode.getGetterCallString();

    // Генерируем код вставки в коллекцию нескольких элементов
    // TODO Заменить класс элементов на нужный
    OCLExpression<EClassifier> source = callExp.getSource();
    String[] addElementsToCollectionCode =
        GeneratorUtils.createAddElementsToCollection(sourceGetterCallString,
            source.getType());

    // Получаем строку по которой обращаемся к модификатору sourceResult
    String sourceSetterCallString = sourceNode.getSetterCallString();
    String setterCode =
        addElementsToCollectionCode[0]
            + sourceSetterCallString
                .replace(GeneratorUtils.REPLACE_CODE_STRING,
                    tempCollectionGetterCode[0]);

    for (int i = 2; i < tempCollectionDeclarationString.length; i++) {
      result.addImportString(tempCollectionDeclarationString[i]);
    }

    for (int i = 1; i < tempCollectionGetterCode.length; i++) {
      result.addImportString(tempCollectionGetterCode[i]);
    }

    for (int i = 1; i < tempCollectionGetterCode.length; i++) {
      result.addImportString(tempCollectionSetterStrings[i]);
    }

    for (int i = 1; i < addElementsToCollectionCode.length; i++) {
      result.addImportString(addElementsToCollectionCode[i]);
    }

    result.setOclString(callExp.toString());
    result.setDeclarationString(tempCollectionDeclarationString[0]);
    result.setSetterCallString(setterCode);

    // Создаем геттер для source->size()
    String resultGetterCallString =
        GeneratorUtils.createMethodCallCode(sourceGetterCallString, "size",
            null);
    result.setGetterCallString(resultGetterCallString);
    return result;
  }

  /**
   * Обработка выражения "Меньше или равно" для всех возможных типов аргументов
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @param result
   * @return
   * @throws NotImplementedMethodException
   * @throws ErrorException
   */
  @SuppressWarnings("unchecked")
  private BooleanExpCodeNode handleOclLessThanEqualCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws NotImplementedMethodException, ErrorException {
    OCLExpression<EClassifier> source = callExp.getSource();
    EClassifier sourceType = source.getType();
    if (sourceType instanceof PrimitiveType) {
      if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
        // TODO обработка выражения "меньше или равно" для типа String
        throw new NotImplementedMethodException(
            "Не реализован метод \"Меньше чем или равно\" для выражений типа String");
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
        return handleLessThanEqualIntegerOperationCallExp(callExp,
            sourceResult, argumentResults, result);
      } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getReal())) {
        return handleLessThanEqualRealOperationCallExp(callExp, sourceResult,
            argumentResults, result);
      } else
        throw new NotImplementedMethodException(
            "Не реализован разбор выражения " + callExp.toString()
                + ". Тип аргумента=" + sourceType.toString());
    } else { // Операнды не примитивноготипа
      // TODO обработка выражения "Меньше или равно" для аргументов не
      // примитивного типа (коллекции)
      // OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
      // IPattern argumentResult = argumentResults.get(0);
      // String argGetterCall =
      // argumentResult.getGetterCallString(arg.toString());
      // String sourceSetterCall =
      // sourceResult.getSetterCallString(source.toString());
      // // Генерируем и добавляем в результат такой код, чтобы результат
      // // операции был равен true
      // String trueString =
      // sourceSetterCall.replace(REPLACE_CODE_STRING, argGetterCall);
      // try {
      // result.setTrueInitialization(callExp.toString(), trueString);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      // // Генерируем и добавляем в результат такой код, чтобы результат
      // // операции был равен false
      // String falseString =
      // sourceSetterCall.replace(REPLACE_CODE_STRING, "null");
      // try {
      // result.setFalseInitialization(callExp.toString(), falseString);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      //
      // // Добавляем строки кода, инициализирующие переменные для корректной
      // // работы сгенерированного кода
      // StringBuilder initCodeString =
      // new StringBuilder(sourceResult.getSetterInitString(source.toString()));
      // initCodeString.append(argumentResult.getGetterInitString(arg.toString()));
      // result.setOperationInitString(callExp.toString(), initCodeString
      // .toString());
      //
      // // Добавляем сгенерированный код из результата аргумента в
      // // результирующий шаблон
      // try {
      // result.setGetterInitString(arg.toString(), argumentResult
      // .getGetterInitString(arg.toString()));
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      // try {
      // result.setGetterCallString(arg.toString(), argGetterCall);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      return result;
    }
  }

  private BooleanExpCodeNode handleLessThanEqualRealOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Double argVal = (Double) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();

    String trueSetterCall =
        ((AChangeableCodeNode) sourceResult).getSetterCallString();
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
        .valueOf(argVal - 1.0));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal + 1.0));
    result.setTrueSection(trueSetterCall);
    result.setFalseSection(falseSetterCall);
    return result;
  }

  private BooleanExpCodeNode handleLessThanEqualIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Integer argVal = (Integer) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall =
        ((AChangeableCodeNode) sourceResult).getSetterCallString();
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
        .valueOf(argVal - 1));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal + 1));
    result.setTrueSection(trueSetterCall);
    result.setFalseSection(falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Меньше или равно" для всех возможных типов аргументов
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @param result
   * @return
   * @throws NotImplementedMethodException
   * @throws ErrorException
   */
  @SuppressWarnings("unchecked")
  private BooleanExpCodeNode handleOclLessCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws NotImplementedMethodException, ErrorException {
    OCLExpression<EClassifier> source = callExp.getSource();
    EClassifier sourceType = source.getType();
    if (sourceType instanceof PrimitiveType) {
      if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
        // TODO обработка выражения "Меньше" для типа String
        throw new NotImplementedMethodException(
            "Не реализован метод \"Меньше чем\" для выражений типа String");
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
        return handleLessIntegerOperationCallExp(callExp, sourceResult,
            argumentResults, result);
      } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getReal())) {
        return handleLessRealOperationCallExp(callExp, sourceResult,
            argumentResults, result);
      } else
        throw new NotImplementedMethodException(
            "Не реализован разбор выражения " + callExp.toString()
                + ". Тип аргумента=" + sourceType.toString());
      // TODO обаботка выржаения "равно" для примитивных типов
    } else { // Операнды не примитивноготипа
      // OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
      // FeatureCallPattern argumentResult = argumentResults.get(0);
      // String argGetterCall =
      // argumentResult.getGetterCallString(arg.toString());
      // String sourceSetterCall =
      // sourceResult.getSetterCallString(source.toString());
      // // Генерируем и добавляем в результат такой код, чтобы результат
      // // операции был равен true
      // String trueString =
      // sourceSetterCall.replace(REPLACE_CODE_STRING, argGetterCall);
      // try {
      // result.setTrueInitialization(callExp.toString(), trueString);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      // // Генерируем и добавляем в результат такой код, чтобы результат
      // // операции был равен false
      // String falseString =
      // sourceSetterCall.replace(REPLACE_CODE_STRING, "null");
      // try {
      // result.setFalseInitialization(callExp.toString(), falseString);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      //
      // // Добавляем строки кода, инициализирующие переменные для корректной
      // // работы сгенерированного кода
      // StringBuilder initCodeString =
      // new StringBuilder(sourceResult.getSetterInitString(source.toString()));
      // initCodeString.append(argumentResult.getGetterInitString(arg.toString()));
      // result.setOperationInitString(callExp.toString(), initCodeString
      // .toString());
      //
      // // Добавляем сгенерированный код из результата аргумента в
      // // результирующий шаблон
      // try {
      // result.setGetterInitString(arg.toString(), argumentResult
      // .getGetterInitString(arg.toString()));
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      // try {
      // result.setGetterCallString(arg.toString(), argGetterCall);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      return result;
    }
  }

  /**
   * Обработка выржаения "Меньше" для аргумнтов типа Real
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private BooleanExpCodeNode handleLessRealOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Double argVal = (Double) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall =
        ((AChangeableCodeNode) sourceResult).getSetterCallString();
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
        .valueOf(argVal - 1));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal + 1.0));
    result.setTrueSection(trueSetterCall);
    result.setFalseSection(falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Меньше" для аргументов типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private BooleanExpCodeNode handleLessIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Integer argVal = (Integer) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String setterCall =
        ((AChangeableCodeNode) sourceResult).getSetterCallString();
    // Первый - такой, чтобы результат выражения саllExp был равен true
    String trueSetterCall =
        setterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal - 1));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        setterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal + 1));
    result.setTrueSection(trueSetterCall);
    result.setFalseSection(falseSetterCall);
    return result;
  }

  private BooleanExpCodeNode handleOclIsUndefinedExpression(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    OCLExpression<EClassifier> source = callExp.getSource();

    // result.setSetterInitString(callExp.toString(), setterInitString);
    // Получаем строку с именем переменной, в которой накапливается результат
    // source
    String referenceString =
        ((AChangeableCodeNode) sourceResult).getSetterCallString();// getReference(source.toString());
    if (referenceString == null) {
      throw new ErrorException(
          "Невозможно выполнить генерацию года для выражения " + callExp
              + ". Не найдена строка для ссылки на объект " + source.toString());
    }
    String trueInitString =
        referenceString.replace(GeneratorUtils.REPLACE_CODE_STRING, "null");
    result.setTrueSection(trueInitString);

    // Генерируем такой код, чтобы операция возвращала false
    // Т.е создаем не-null объект
    try {
      String nonNullObjectString =
          GeneratorUtils.createNonNullObjectInitializationString(source
              .getType());
      String falseInitString =
          referenceString.replace(GeneratorUtils.REPLACE_CODE_STRING,
              nonNullObjectString);
      result.setFalseSection(falseInitString);
    } catch (ErrorException e) {
      LogManager.logError(e.getMessage());
    }

    // String sourceInitString = result.getSetterInitString(source.toString());
    // result.setSetterInitString(source.toString(), sourceInitString);
    return result;
  }

  @SuppressWarnings( { "unchecked" })
  private BooleanExpCodeNode handleEqualOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException, NotImplementedMethodException {

    OCLExpression<EClassifier> source = callExp.getSource();

    EClassifier sourceType = source.getType();
    if (sourceType instanceof PrimitiveType) {
      if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getString())) {
        // TODO обработка выражения "Равно" для типа String
        throw new NotImplementedMethodException(
            "Не реализован метод \"Равно\" для выражений типа String");
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getInteger())) {
        return handleEqualIntegerOperationCallExp(callExp, sourceResult,
            argumentResults, result);
      } else if (sourceType.equals(OCLStandardLibraryImpl.INSTANCE.getReal())) {
        return handleEqualRealOperationCallExp(callExp, sourceResult,
            argumentResults, result);
      } else if (sourceType
          .equals(OCLStandardLibraryImpl.INSTANCE.getBoolean())) {
        return handleEqualBooleanOperationCallExp(callExp, sourceResult,
            argumentResults, result);
      } else
        throw new NotImplementedMethodException(
            "Не реализован разбор выражения " + callExp.toString()
                + ". Тип аргумента=" + sourceType.toString());
    } else { // Операнды не примитивноготипа
      // OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
      // FeatureCallPattern argumentResult = argumentResults.get(0);
      // String argGetterCall =
      // argumentResult.getGetterCallString(arg.toString());
      // String sourceSetterCall =
      // sourceResult.getSetterCallString(source.toString());
      // // Генерируем и добавляем в результат такой код, чтобы результат
      // // операции был равен true
      // String trueString =
      // sourceSetterCall.replace(REPLACE_CODE_STRING, argGetterCall);
      // try {
      // result.setTrueInitialization(callExp.toString(), trueString);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      // // Генерируем и добавляем в результат такой код, чтобы результат
      // // операции был равен false
      // String falseString =
      // sourceSetterCall.replace(REPLACE_CODE_STRING, "null");
      // try {
      // result.setFalseInitialization(callExp.toString(), falseString);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      //
      // // Добавляем строки кода, инициализирующие переменные для корректной
      // // работы сгенерированного кода
      // StringBuilder initCodeString =
      // new StringBuilder(sourceResult.getSetterInitString(source.toString()));
      // initCodeString.append(argumentResult.getGetterInitString(arg.toString()));
      // try {
      // result.setSetterInitString(callExp.toString(), initCodeString
      // .toString());
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      // // result.setOperationInitString(callExp.toString(),
      // // initCodeString.toString());
      //
      // // Добавляем сгенерированный код из результата аргумента в
      // // результирующий шаблон
      // try {
      // result.setGetterInitString(arg.toString(), argumentResult
      // .getGetterInitString(arg.toString()));
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }
      // try {
      // result.setGetterCallString(arg.toString(), argGetterCall);
      // } catch (ErrorException e) {
      // ErrorManager.addError(e.getMessage());
      // }

      return result;
    }
  }

  /**
   * Обработка выражения "Равно" для типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private BooleanExpCodeNode handleEqualIntegerOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Integer argVal = (Integer) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String setterCall =
        ((AChangeableCodeNode) sourceResult).getSetterCallString();
    // Первый - такой, чтобы результат выражения саllExp был равен true
    String trueSetterCall =
        setterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        setterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal + 1));

    // result.setSetterInitString(source.toString(), propertySetterInitString)
    result.setTrueSection(trueSetterCall);
    result.setFalseSection(falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Равно" для типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private BooleanExpCodeNode handleEqualRealOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Double argVal = (Double) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall =
        ((AChangeableCodeNode) sourceResult).getSetterCallString();
    // Первый - такой, чтобы результат выражения саllExp был равен true
    trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
        .valueOf(argVal));
    // Второй - такой, чтобы результат выражения саllExp был равен false
    String falseSetterCall =
        trueSetterCall.replace(GeneratorUtils.REPLACE_CODE_STRING, String
            .valueOf(argVal + 1.0));
    result.setTrueSection(trueSetterCall);
    result.setFalseSection(falseSetterCall);
    return result;
  }

  /**
   * Обработка выражения "Равно" для типа Integer
   * 
   * @param callExp
   * @param sourceResult
   * @param argumentResults
   * @return
   * @throws ErrorException
   */
  private BooleanExpCodeNode handleEqualBooleanOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, IPattern sourceResult,
      List<IPattern> argumentResults, BooleanExpCodeNode result)
      throws ErrorException {

    // Вычисляем значение в правой части выражения
    OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
    Boolean argVal = (Boolean) OclEcoreUtils.evaluateExpression(arg, ocl);

    // Создаем 2 куска кода...
    // OCLExpression<EClassifier> source = callExp.getSource();
    String trueSetterCall =
        ((BooleanExpCodeNode) sourceResult).getTrueSection();
    String falseSetterCall =
        ((BooleanExpCodeNode) sourceResult).getFalseSection();
    if (argVal) {
      result.setTrueSection(trueSetterCall);
      result.setFalseSection(falseSetterCall);
    } else {
      result.setTrueSection(falseSetterCall);
      result.setFalseSection(trueSetterCall);
    }
    return result;
  }
}
